package assg.funnycells.server;

import java.net.*;
import java.io.*;
import java.util.*;

import assg.funnycells.util.*;

public class Cell implements Runnable {
    public final static String UNKNOWN = "UNKNOWN";

    public final static int UNDEF = -100;

    public final static String NEWCELL = "NEWCELL";
    public final static String MOVECELL = "MOVECELL";
    public final static String DROPCHEM = "DROPCHEM";
    public final static String SENSECHEM = "SENSECHEM";
    public final static String DROPENERGY = "DROPENERGY";

    public final static String ERROR = "ERROR";
    public final static String OK = "OK";
    public final static String CHEMICALS = "CHEMICALS";
    public final static String WALL = "WALL";

    public final static String CHANGESTATE = "CHANGESTATE";
    public final static String EXIT = "EXIT";

    Socket soc;
    ActivityDisplay display;
    PrintWriter out;
    BufferedReader in;
    ServerState serverState;
    Logger logger;

    String cellId;
    CellState cellState;
    boolean closeConn;

    public Cell(Socket soc, ServerState serverState, ActivityDisplay display) throws Exception {
	this.soc = soc;
	this.display = display;
	this.serverState = serverState;
	this.logger = Logger.getDefaultLogger();
	this.out = new PrintWriter(soc.getOutputStream());
	this.in = new BufferedReader(new InputStreamReader(soc.getInputStream()));

	this.cellId = UNKNOWN;
	this.cellState = new CellState();
	this.closeConn = false;

	Thread t = new Thread(this);
	t.start();
    }

    public void run() {
	try {
	    ArrayList<String> arr = new ArrayList<String>();
	    String readline;
	    while((readline = in.readLine()) != null) {
		while(readline != null && !readline.equals("")) {
		    arr.add(readline);
		    readline = in.readLine();
		}
		if(readline == null)
		    break;
		else {
		    constructPacket(arr);
		    arr.clear();
		}
	    }
	    in.close();

	    synchronized(serverState) {
		if(!closeConn)
		    serverState.cellCrash(cellId);
	    }

	} catch(Exception e) {
	    logger.infooverride("Socket crash: " + cellId + " - " + e.getMessage() + ": look at exception below");
	    logger.error("Socket crash: " + cellId, e); // XXX
	}

	synchronized(serverState) {
	    if(!closeConn)
		serverState.cellCrash(cellId);
	}
    }

    public void safeCloseConnection() {
	closeConn = true;
    }

    public String id() {
	return cellId;
    }

    public void setId(String cellId) {
	this.cellId = cellId;
    }

    public CellState getCellState() {
	return cellState;
    }

    public void sendMessage(String[] arr) throws Exception {
	ChemOperations.sendMessage(out, arr);
    }
    
    protected void constructPacket(ArrayList<String> arr) throws Exception {
	if(arr.size() > 0) {
	    String readline = arr.get(0).trim();
	    if(readline.startsWith(NEWCELL) && cellId.equals(UNKNOWN)) {
		newCell(arr);
	    } else if(readline.startsWith(MOVECELL) && !cellId.equals(UNKNOWN)) {
		moveCell(arr);
	    } else if(readline.startsWith(DROPCHEM) && !cellId.equals(UNKNOWN)) {
		dropChem(arr);
	    } else if(readline.startsWith(SENSECHEM) && !cellId.equals(UNKNOWN)) {
		senseChem(arr);
	    } else if(readline.startsWith(DROPENERGY)) {
		dropEnergy(arr);
	    } else {
		ChemOperations.sendMessage(out, new String[]{ERROR, "Unknown type"});
	    }
	} else {
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Empty message"});
	}
    }

    // NEWCELL=cellid
    // TYPE=0
    // X=10
    // Y=15
    // ENERGY=default
    protected void newCell(ArrayList<String> arr) throws Exception {
	if(arr.size() == 5) {
	    String readline = arr.get(0).trim();
	    String val = StringOperations.parseVal(readline);
	    String cellId = "";
	    if(!val.equals("") && !val.equals(UNKNOWN))
		cellId = val;

	    readline = arr.get(1).trim();
	    val = StringOperations.parseVal(readline);
	    int type = -1;
	    if(!val.equals(""))
		type = StringOperations.parseInt(val);
	    
	    readline = arr.get(2).trim();
	    val = StringOperations.parseVal(readline);
	    int x = -1, y = -1;
	    if(!val.equals(""))
		x = StringOperations.parseInt(val);
	    
	    readline = arr.get(3).trim();
	    val = StringOperations.parseVal(readline);
	    if(!val.equals(""))
		y = StringOperations.parseInt(val);

	    int energy = -1;
	    readline = arr.get(4).trim();
	    val = StringOperations.parseVal(readline);
	    if(!val.equals(""))
		energy = StringOperations.parseInt(val);

	    if(type == CellState.ENERGY) {
		ChemOperations.sendMessage(out, new String[]{OK, "x=" + x, "y=" + y});
	    } else {
		synchronized(serverState) {
		    logger.info(NEWCELL + " received: " + cellId + " - " + type + " (" + x + "," + y + ") " + energy);
		    serverState.newCell(cellId, type, x, y, energy, this);
		}
	    }

	} else {
	    logger.info("Malformed error sent for " + NEWCELL); 
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Malformed " + NEWCELL + " message"});
	}
    }

    // MOVECELL
    // INCX=10
    // INCY=15
    protected void moveCell(ArrayList<String> arr) throws Exception {
	if(arr.size() == 3) {
	    String readline = arr.get(1).trim();
	    String val = StringOperations.parseVal(readline);
	    int incx = -1, incy = -1;
	    if(!val.equals(""))
		incx = StringOperations.parseInt(val);
	    
	    readline = arr.get(2).trim();
	    val = StringOperations.parseVal(readline);
	    if(!val.equals(""))
		incy = StringOperations.parseInt(val);

	    synchronized(serverState) {
		logger.info(MOVECELL + " received: " + cellId + ": " + incx + "," + incy);
		serverState.moveCell(cellId, incx, incy);
	    }

	} else {
	    logger.info("Malformed error sent for " + MOVECELL);
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Malformed " + MOVECELL + " message"});
	}
    }

    // DROPCHEM
    // chemical string
    // OFFX=10
    // OFFY=10
    // COLOR=000000
    protected void dropChem(ArrayList<String> arr) throws Exception {
	if(arr.size() == 5) {
	    String chemStr = arr.get(1);

	    String readline = arr.get(2).trim();
	    String val = StringOperations.parseVal(readline);
	    int offx = -1, offy = -1;
	    if(!val.equals(""))
		offx = StringOperations.parseInt(val);
	    
	    readline = arr.get(3).trim();
	    val = StringOperations.parseVal(readline);
	    if(!val.equals(""))
		offy = StringOperations.parseInt(val);

	    readline = arr.get(4).trim();
	    String color = StringOperations.parseVal(readline);
	    
	    synchronized(serverState) {
		logger.info(DROPCHEM + " received: " + cellId + ": " + chemStr);
		serverState.dropChem(cellId, chemStr, offx, offy, color);
	    }

	} else {
	    logger.info("Malformed error sent for " + DROPCHEM);
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Malformed " + DROPCHEM + " message"});
	}
    }

    // SENSECHEM
    // chemical string match
    // MAX=4
    protected void senseChem(ArrayList<String> arr) throws Exception {
	if(arr.size() == 3 || arr.size() == 5) {
	    String chemStr = arr.get(1);

	    String readline = arr.get(2);
	    String val = StringOperations.parseVal(readline);
	    int maxChem = -1;
	    if(!val.equals(""))
		maxChem = StringOperations.parseInt(val);
	    
	    int offX = UNDEF;
	    int offY = UNDEF;
	    if(arr.size() == 5) {
		readline = arr.get(3);
		val = StringOperations.parseVal(readline);
		if(!val.equals(""))
		    offX = StringOperations.parseInt(val);
		
		readline = arr.get(4);
		val = StringOperations.parseVal(readline);
		if(!val.equals(""))
		    offY = StringOperations.parseInt(val);
	    }

	    synchronized(serverState) {
		logger.info(SENSECHEM + " received: " + cellId + ": " + chemStr + ", " + maxChem + ", " + offX + ", " + offY);
		serverState.senseChem(cellId, chemStr, maxChem, offX, offY);
	    }

	} else {
	    logger.info("Malformed error sent for " + SENSECHEM);
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Malformed " + SENSECHEM + " message"});
	}	
    }

    // DROPENERGY
    // energy chemical
    // x=4
    // y=5
    protected void dropEnergy(ArrayList<String> arr) throws Exception {
	if(arr.size() == 4) {
	    String chemStr = arr.get(1);

	    String readline = arr.get(2).trim();
	    String val = StringOperations.parseVal(readline);
	    int x = -1, y = -1;
	    if(!val.equals(""))
		x = StringOperations.parseInt(val);
	    
	    readline = arr.get(3).trim();
	    val = StringOperations.parseVal(readline);
	    if(!val.equals(""))
		y = StringOperations.parseInt(val);

	    synchronized(serverState) {
		logger.info(DROPENERGY + " received: " + cellId + ": " + chemStr + " (" + x + ", " + y + ")");
		serverState.dropEnergy(cellId, this, chemStr, x, y);
	    }

	} else {
	    logger.info("Malformed error sent for " + DROPENERGY);
	    ChemOperations.sendMessage(out, new String[]{ERROR, "Malformed " + DROPENERGY + " message"});
	}
	
    }
}